package com.hmall.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.hmall.search.pojo.Item;
import com.hmall.search.pojo.ItemDoc;
import com.hmall.search.pojo.PageDTO;
import com.hmall.search.pojo.ResultParam;
import com.hmall.search.service.SearchService;

import org.apache.commons.io.filefilter.AgeFileFilter;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.Param;
import org.apache.logging.log4j.message.ReusableMessage;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.core.CountRequest;
import org.elasticsearch.client.core.CountResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;

import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;

import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.ui.context.Theme;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *
 */
@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private RestHighLevelClient client;

    //布尔查询
    @Override
    public PageDTO getList(ResultParam param) {
        try {
            SearchRequest request = new SearchRequest("hmall");
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
            //查询总数
            CountRequest countRequest = new CountRequest("hmall");

            //key
            if (StringUtils.isNotBlank(param.getKey())) {
                boolQuery.must(QueryBuilders.matchQuery("all", param.getKey()));
            }
            //品牌
            if (StringUtils.isNotBlank(param.getBrand())) {
                boolQuery.filter(QueryBuilders.matchQuery("brand", param.getBrand()));
            }
            //分类
            if (StringUtils.isNotBlank(param.getCategory())) {
                boolQuery.filter(QueryBuilders.matchQuery("category", param.getCategory()));
            }
            //价格
            if (param.getMaxPrice() != null && param.getMinPrice() != null) {
                boolQuery.filter(
                        QueryBuilders.rangeQuery("price")
                                .lt(param.getMaxPrice() * 100)
                                .gte(param.getMinPrice() * 100)
                );
            }

            request.source().query(boolQuery);
            countRequest.query(boolQuery);
            // 2.2.高亮
            request.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));

            //分页查询
            int startPage = (param.getPage() - 1) * param.getSize();
            request.source().from(startPage).size(param.getSize());
            //排序
            if (!param.getSortBy().equals("default")) {
                request.source().sort(param.getSortBy());
            }
            //布尔查询
            SearchResponse search = client.search(request, RequestOptions.DEFAULT);
            SearchHits hits = search.getHits();
            //查询总数
            CountResponse count = client.count(countRequest, RequestOptions.DEFAULT);

            long count1 = count.getCount();
            //序列化
            SearchHit[] hi = hits.getHits();
            PageDTO pageDTO = new PageDTO<>();
            ArrayList<Item> list = new ArrayList<>();
            pageDTO.setTotal(count1);
            for (SearchHit fields : hi) {
                String json = fields.getSourceAsString();
                Item item = JSON.parseObject(json, Item.class);
                // 获取高亮结果
                Map<String, HighlightField> highlightFields = fields.getHighlightFields();
                if (!CollectionUtils.isEmpty(highlightFields)) {
                    // 根据字段名获取高亮结果
                    HighlightField highlightField = highlightFields.get("name");
                    if (highlightField != null) {
                        // 获取高亮值
                        String name = highlightField.getFragments()[0].string();
                        // 覆盖非高亮结果
                        item.setName(name);
                    }
                }
                list.add(item);
            }
            pageDTO.setList(list);

            return pageDTO;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //自动补全
    @Override
    public List<String> suggestion(String key) {
        try {
            //构造查询
            SearchRequest request = new SearchRequest("hmall");
            request.source().suggest(new SuggestBuilder()
                    .addSuggestion("suggestions",
                            SuggestBuilders.completionSuggestion("suggestion")
                                    .prefix(key)
                                    .skipDuplicates(true)
                                    .size(10)));
            //发起请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            //解析
            Suggest suggest = response.getSuggest();
            CompletionSuggestion suggestions = suggest.getSuggestion("suggestions");

            List<CompletionSuggestion.Entry.Option> options = suggestions.getOptions();
            List<String> list = new ArrayList<>();
            for (CompletionSuggestion.Entry.Option option : options) {
                String str = option.getText().toString();
                list.add(str);
            }
            return list;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    //聚合查询
    @Override
    public Map<String, List<String>> filters(ResultParam param) {

        try {
            SearchRequest request = new SearchRequest("hmall");
            request.source().size(0);

            request.source().aggregation(AggregationBuilders
                    .terms("brandAgg")
                    .field("brand")
                    .size(100));
            request.source().aggregation(AggregationBuilders
                    .terms("categoryAgg")
                    .field("category")
                    .size(50));

            if (StringUtils.isNotBlank(param.getKey())) {
                request.source().query(QueryBuilders.matchQuery("all", param.getKey()));
            }

            SearchResponse search = client.search(request, RequestOptions.DEFAULT);
            HashMap<String, List<String>> map = new HashMap();
            List list1 = getAggArgs(search, "brandAgg");
            map.put("brand",list1);
            List list2 = getAggArgs(search, "categoryAgg");
            map.put("category",list2);
            return map;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    //聚合查询结果解析 抽取方法
    private List getAggArgs(SearchResponse search,String bucketName) {
        List<String> list = new ArrayList<>();
        Aggregations aggregations = search.getAggregations();
        Terms brandTerms = aggregations.get(bucketName);
        List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();
        for (Terms.Bucket bucket : buckets) {
            String brand = bucket.getKeyAsString();
            list.add(brand);
        }
        return list;
    }

}
